package Code.Basic.Entity;

import org.jrabbit.base.graphics.transforms.Vector2d;
import org.jrabbit.base.math.geom.Geometry;
import org.jrabbit.standard.game.graphics.skins.animation.AnimatedSkin;
import org.jrabbit.standard.game.objects.specialized.AnimatedSprite;

import Code.Basic.World.PhysicsInfo;

import net.phys2d.math.Vector2f;
import net.phys2d.raw.Body;
import net.phys2d.raw.shapes.Box;
import net.phys2d.raw.shapes.Circle;
import net.phys2d.raw.shapes.DynamicShape;
import net.phys2d.raw.shapes.Polygon;

public class PhysicsSprite extends AnimatedSprite
{
	protected Body body;
	protected Geometry shape;
	protected float mass;
	
	public PhysicsSprite(String skin)
	{
		this(new AnimatedSkin(skin));
	}

	public PhysicsSprite(AnimatedSkin skin)
	{
		super(skin);
		setUpPhysics();
	}

	private void setUpPhysics()
	{
		mass = 1f;
		setToRectangle((float) getWidth(), (float) getHeight());
		body.setDamping(0.1f);
		body.setRotDamping(0.1f);
	}
	
	public Body getBody()
	{
		return body;
	}
	
	public Geometry getGeometry()
	{
		return shape;
	}
	
	public boolean intersects(Geometry geom)
	{
		if(shape.intersects(geom))
		{
			return true;
		}
		return false;
	}
	
	public void setToRectangle(float width, float height)
	{
		body = new Body(new Box((float) (width * scalar.getScaleX() / PhysicsInfo.SCALE),
				(float) (height * scalar.getScaleY() / PhysicsInfo.SCALE)), mass);
		shape = new Geometry(width, height);
	}
	
	public void setToCircle(float radius)
	{
		body.set(new Circle(radius * (float) (scalar.getScale() / PhysicsInfo.SCALE)), mass);
		
		// Apparently, circles are much slower to detect collision than polygons. So, we use an octagon.
		shape = new Geometry(new double[][] {{radius * 0.66, 0}, {radius * 1.34, 0}, {radius * 2, radius * 0.66},
										{radius * 2, radius * 1.34}, {radius * 1.34, radius * 2}, 
										{radius * 0.66, radius * 2}, {0, radius * 1.34}, {0, radius * 0.66}});
	}
	
	public void setToPolygon(double[][] verticeData)
	{
		shape = new Geometry(verticeData);
		double width = shape.baseShape().getBounds2D().getWidth();
		double height = shape.baseShape().getBounds2D().getHeight();
		
		// We need to do some shuffling here because Geometry vertices are clockwise,
		// while physics Polygons are counterclockwise. Plus, the vertice offsets are different
		// for each.
		Vector2f[] vectors = new Vector2f[verticeData.length];
		for(int i = 0; i < verticeData.length; i++)
		{
			int j = (verticeData.length - 1) - i;
			vectors[i] = new Vector2f((float) (verticeData[j][0] - (width / 2)) / (PhysicsInfo.SCALE),
					(float) (verticeData[j][1] - (height / 2)) / PhysicsInfo.SCALE);
		}
		body.set(new Polygon(vectors), mass);
		body.setPosition((float) location.x() / PhysicsInfo.SCALE, (float) location.y() / PhysicsInfo.SCALE);
	}
	
	public float getMass()
	{
		return mass;
	}
	
	public void setMass(float m)
	{
		float damping = body.getDamping() / mass;
		float rotDamping = body.getRotDamping() / mass;
		mass = m;
		DynamicShape s = (DynamicShape) body.getShape();
		body.set(s, mass);
		body.setDamping(damping);
		body.setRotDamping(rotDamping);
	}
	
	public void adjust()
	{
		setLocation(body.getPosition().getX() * PhysicsInfo.SCALE, body.getPosition().getY() * PhysicsInfo.SCALE);
		setRotation(body.getRotation() * 180 / Math.PI);
		shape.updateShape(location, rotation, scalar);
	}
	
	public void moveForward(float force)
	{
		float xF = (float) Math.cos(body.getRotation()) * force;
		float yF = (float) Math.sin(body.getRotation()) * force;
		body.addForce(new Vector2f(xF, yF));
	}
	
	public void moveSideways(float force)
	{
		float xF = (float) -Math.sin(body.getRotation()) * force;
		float yF = (float) Math.cos(body.getRotation()) * force;
		body.addForce(new Vector2f(xF, yF));
	}
	
	public void update(int delta)
	{
		adjust();
		super.update(delta);
	}
	
	
	// The following are methods are rewritten to also effect the physics body.
	
	public void updatePhysicsPosition()
	{
		body.setPosition((float) location.x() / PhysicsInfo.SCALE, (float) location.y() / PhysicsInfo.SCALE);
	}
	
	public void setLocation(Vector2d point)
	{
		location.set(point);
		updatePhysicsPosition();
	}
	
	public void setLocation(double x, double y)
	{
		location.set(x, y);
		updatePhysicsPosition();
	}
	
	public void translate(Vector2d vector)
	{
		location.add(vector);
		updatePhysicsPosition();
	}
	
	public void translate(double x, double y)
	{
		location.add(x, y);
		updatePhysicsPosition();
	}
	
	public void updatePhysicsRotation()
	{
		body.setRotation((float) rotation.theta());
	}
	
	public void setRotation(double rot)
	{
		rotation.set(rot);
		updatePhysicsRotation();
	}
	
	public void rotate(double r)
	{
		rotation.rotate(r);
		updatePhysicsRotation();
	}
}
